// wglapplayer.hpp
//

#ifndef _WGLAPPLAYER_HPP
#define _WGLAPPLAYER_HPP

#include "wgl/wglbase.hpp"

#define WGL_GET_APP_LAYER_FUNC_INFOS(id) (wgl::APP_LAYER_FUNC_INFOS[WGL_ENUM_VALUE(id)])

#include "wgl/wgl_gltypes.hpp"
#include "wgl/wgl_glfuncs.hpp"

#include "wgl/wglapplayer.inl.hpp"

namespace wgl
{
  typedef size_t AppLayerLoaderID;


  
  // AppLayer
  //
  struct WGL_API AppLayer
  {
    AppLayer () = default;

    AppLayerLoaderID loader_id{};
    AppLayer *chain{nullptr};
    void *data{nullptr};
    
    AppLayerVTable vtable;
  };

  static_assert(std::is_trivially_copyable<AppLayer>::value);

  
  // AppLayerMaster
  //
  class WGL_API AppLayerMaster
  {
    friend AppLayerVTable;
    
  public:
    static AppLayer &get_current_layer ( AppLayerLoaderID loader_id );
    
    void reset ();
    void make_current ();

  private:
    static AppLayerMaster * WGL_API current_master;

    bool loaded{false};
    AppLayer root_layer;
    std::vector<AppLayer *> layers_by_id{};
    
    void load ();
  };



  // AppLayerLoader
  //
  class WGL_API AppLayerLoader
  {
  public:
    static AppLayerLoader &get_loader ( AppLayerLoaderID loader_id )
    {
      WGL_ASSERT(loader_id > 0 && loader_id < registered_loaders.size());
      return *registered_loaders[loader_id];
    }

    static AppLayerLoader &get_loader ( const std::string &name )
    {
      if (!registered_loaders_by_name.contains(name))
        {
          WGL_ERROR("unknown application layer: '%s'", name.c_str());
          WGL_FATAL("[todo] fatal error"); //return;
        }
      return *registered_loaders_by_name[name];
    }

    template<class LoaderT>
    static AppLayerLoaderID register_loader ( const std::string &name )
    {
      WGL_ASSERT(!registered_loaders_by_name.contains(name));
      
      AppLayerLoaderID id = registered_loaders.size();
      AppLayerLoader *loader = new LoaderT;

      registered_loaders.emplace_back(loader);
      registered_loaders_by_name.emplace(name, loader);
      
      return id;
    }

    virtual AppLayerLoaderID get_loader_id () = 0;
    virtual void load_layer ( AppLayer &layer ) = 0;
    virtual void chain_layer ( AppLayer &layer );
    
  private:
    static std::vector<AppLayerLoader *> WGL_API registered_loaders;
    static std::unordered_map<std::string, AppLayerLoader *> WGL_API registered_loaders_by_name;
  };



  // AppLayerLoaderImpl
  //
  template<class LoaderT>
  class WGL_API AppLayerLoaderImpl : public AppLayerLoader
  {
  public:
    static const AppLayerLoaderID WGL_API LOADER_ID;

    virtual AppLayerLoaderID get_loader_id () { return LoaderT::LOADER_ID; }
  };



  // AppLayerLoaderDynamic
  //
  class WGL_API AppLayerLoaderDynamic : public AppLayerLoaderImpl<AppLayerLoaderDynamic>
  {
  public:
    virtual void load_layer ( AppLayer &layer );
  };
}

#endif
